/**
 * 消息加密工具类
 * 基于Web Crypto API实现AES-256-GCM加密
 * 
 * @author RickPan Team
 * @version 1.0
 * @since 2025-07-19
 */

import type { EncryptedMessage, DecryptedMessage } from '@/types/encryption'

// 重新导出类型以保持向后兼容
export type { EncryptedMessage, DecryptedMessage }

/**
 * 消息加密核心类
 */
export class MessageEncryption {
  private static readonly ALGORITHM = 'AES-GCM'
  private static readonly KEY_LENGTH = 256
  private static readonly IV_LENGTH = 12
  private static readonly TAG_LENGTH = 128

  /**
   * 检查浏览器是否支持Web Crypto API
   */
  static isSupported(): boolean {
    return typeof window !== 'undefined' && 
           'crypto' in window && 
           'subtle' in window.crypto &&
           typeof window.crypto.subtle.encrypt === 'function'
  }

  /**
   * 将Base64字符串转换为ArrayBuffer
   */
  private static base64ToArrayBuffer(base64: string): ArrayBuffer {
    const binaryString = atob(base64)
    const bytes = new Uint8Array(binaryString.length)
    for (let i = 0; i < binaryString.length; i++) {
      bytes[i] = binaryString.charCodeAt(i)
    }
    return bytes.buffer
  }

  /**
   * 将ArrayBuffer转换为Base64字符串
   */
  private static arrayBufferToBase64(buffer: ArrayBuffer): string {
    const bytes = new Uint8Array(buffer)
    let binary = ''
    for (let i = 0; i < bytes.byteLength; i++) {
      binary += String.fromCharCode(bytes[i])
    }
    return btoa(binary)
  }

  /**
   * 导入AES密钥
   */
  private static async importKey(keyBase64: string): Promise<CryptoKey> {
    const keyBuffer = this.base64ToArrayBuffer(keyBase64)
    
    return await window.crypto.subtle.importKey(
      'raw',
      keyBuffer,
      { name: this.ALGORITHM },
      false,
      ['encrypt', 'decrypt']
    )
  }

  /**
   * 生成随机IV
   */
  private static generateIV(): Uint8Array {
    return window.crypto.getRandomValues(new Uint8Array(this.IV_LENGTH))
  }

  /**
   * 使用指定的IV加密消息内容
   *
   * @param content 要加密的内容
   * @param teamKeyBase64 团队密钥（Base64格式）
   * @param ivBase64 指定的IV（Base64格式）
   * @returns 加密结果
   */
  static async encryptWithIV(content: string, teamKeyBase64: string, ivBase64: string): Promise<EncryptedMessage> {
    try {
      if (!this.isSupported()) {
        throw new Error('浏览器不支持Web Crypto API')
      }

      if (!content || !teamKeyBase64 || !ivBase64) {
        throw new Error('内容、密钥或IV不能为空')
      }

      // 1. 导入密钥
      const key = await this.importKey(teamKeyBase64)

      // 2. 使用指定的IV
      const iv = this.base64ToArrayBuffer(ivBase64)

      // 3. 将内容转换为ArrayBuffer
      const contentBuffer = new TextEncoder().encode(content)

      // 4. 执行加密
      const encryptedBuffer = await window.crypto.subtle.encrypt(
        {
          name: this.ALGORITHM,
          iv: iv,
          tagLength: this.TAG_LENGTH
        },
        key,
        contentBuffer
      )

      // 5. 转换为Base64
      const ciphertext = this.arrayBufferToBase64(encryptedBuffer)

      return {
        ciphertext,
        iv: ivBase64,
        algorithm: this.ALGORITHM
      }

    } catch (error) {
      console.error('❌ 使用指定IV加密失败:', error)
      throw new Error(`使用指定IV加密失败: ${error instanceof Error ? error.message : '未知错误'}`)
    }
  }

  /**
   * 加密消息内容
   *
   * @param content 要加密的内容
   * @param teamKeyBase64 团队密钥（Base64格式）
   * @returns 加密结果
   */
  static async encryptMessage(content: string, teamKeyBase64: string): Promise<EncryptedMessage> {
    try {
      if (!this.isSupported()) {
        throw new Error('浏览器不支持Web Crypto API')
      }

      if (!content || !teamKeyBase64) {
        throw new Error('内容和密钥不能为空')
      }

      // 1. 导入密钥
      const key = await this.importKey(teamKeyBase64)

      // 2. 生成随机IV
      const iv = this.generateIV()

      // 3. 将内容转换为ArrayBuffer
      const contentBuffer = new TextEncoder().encode(content)

      // 4. 执行加密
      const encryptedBuffer = await window.crypto.subtle.encrypt(
        {
          name: this.ALGORITHM,
          iv: iv,
          tagLength: this.TAG_LENGTH
        },
        key,
        contentBuffer
      )

      // 5. 转换为Base64格式
      const ciphertext = this.arrayBufferToBase64(encryptedBuffer)
      const ivBase64 = this.arrayBufferToBase64(iv.buffer)

      return {
        ciphertext,
        iv: ivBase64,
        algorithm: 'AES-256-GCM'
      }

    } catch (error) {
      console.error('❌ 消息加密失败:', error)
      throw new Error(`消息加密失败: ${error instanceof Error ? error.message : '未知错误'}`)
    }
  }

  /**
   * 解密消息内容
   * 
   * @param encryptedData 加密数据
   * @param teamKeyBase64 团队密钥（Base64格式）
   * @returns 解密后的内容
   */
  static async decryptMessage(encryptedData: EncryptedMessage, teamKeyBase64: string): Promise<string> {
    try {
      if (!this.isSupported()) {
        throw new Error('浏览器不支持Web Crypto API')
      }

      if (!encryptedData.ciphertext || !encryptedData.iv || !teamKeyBase64) {
        throw new Error('加密数据或密钥不完整')
      }

      // 1. 导入密钥
      const key = await this.importKey(teamKeyBase64)

      // 2. 转换数据格式
      const ciphertextBuffer = this.base64ToArrayBuffer(encryptedData.ciphertext)
      const ivBuffer = this.base64ToArrayBuffer(encryptedData.iv)

      // 3. 执行解密
      const decryptedBuffer = await window.crypto.subtle.decrypt(
        {
          name: this.ALGORITHM,
          iv: ivBuffer,
          tagLength: this.TAG_LENGTH
        },
        key,
        ciphertextBuffer
      )

      // 4. 转换为字符串
      const decryptedContent = new TextDecoder().decode(decryptedBuffer)

      return decryptedContent

    } catch (error) {
      console.error('❌ 消息解密失败:', error)
      throw new Error(`消息解密失败: ${error instanceof Error ? error.message : '未知错误'}`)
    }
  }

  /**
   * 批量加密消息（用于文件名等多个字段）
   * 
   * @param data 要加密的数据对象
   * @param teamKeyBase64 团队密钥
   * @returns 加密后的数据
   */
  static async encryptMultipleFields(
    data: { content?: string; fileName?: string },
    teamKeyBase64: string
  ): Promise<{ encryptedContent?: EncryptedMessage; encryptedFileName?: EncryptedMessage }> {
    const result: { encryptedContent?: EncryptedMessage; encryptedFileName?: EncryptedMessage } = {}

    if (data.content) {
      result.encryptedContent = await this.encryptMessage(data.content, teamKeyBase64)
    }

    if (data.fileName) {
      result.encryptedFileName = await this.encryptMessage(data.fileName, teamKeyBase64)
    }

    return result
  }

  /**
   * 批量解密消息
   * 
   * @param encryptedData 加密的数据对象
   * @param teamKeyBase64 团队密钥
   * @returns 解密后的数据
   */
  static async decryptMultipleFields(
    encryptedData: { encryptedContent?: EncryptedMessage; encryptedFileName?: EncryptedMessage },
    teamKeyBase64: string
  ): Promise<DecryptedMessage> {
    const result: DecryptedMessage = { content: '' }

    if (encryptedData.encryptedContent) {
      result.content = await this.decryptMessage(encryptedData.encryptedContent, teamKeyBase64)
    }

    if (encryptedData.encryptedFileName) {
      result.fileName = await this.decryptMessage(encryptedData.encryptedFileName, teamKeyBase64)
    }

    return result
  }

  /**
   * 验证加密数据的完整性
   * 
   * @param encryptedData 加密数据
   * @returns 是否有效
   */
  static validateEncryptedData(encryptedData: EncryptedMessage): boolean {
    return !!(
      encryptedData &&
      encryptedData.ciphertext &&
      encryptedData.iv &&
      encryptedData.algorithm === 'AES-256-GCM'
    )
  }

  /**
   * 生成测试用的随机密钥（仅用于开发测试）
   */
  static async generateTestKey(): Promise<string> {
    if (!this.isSupported()) {
      throw new Error('浏览器不支持Web Crypto API')
    }

    const key = await window.crypto.subtle.generateKey(
      {
        name: this.ALGORITHM,
        length: this.KEY_LENGTH
      },
      true,
      ['encrypt', 'decrypt']
    )

    const keyBuffer = await window.crypto.subtle.exportKey('raw', key)
    return this.arrayBufferToBase64(keyBuffer)
  }

  /**
   * 性能测试：加密解密速度测试
   */
  static async performanceTest(testData: string, teamKey: string, iterations: number = 100): Promise<{
    encryptTime: number
    decryptTime: number
    avgEncryptTime: number
    avgDecryptTime: number
  }> {
    const encryptTimes: number[] = []
    const decryptTimes: number[] = []

    for (let i = 0; i < iterations; i++) {
      // 测试加密
      const encryptStart = performance.now()
      const encrypted = await this.encryptMessage(testData, teamKey)
      const encryptEnd = performance.now()
      encryptTimes.push(encryptEnd - encryptStart)

      // 测试解密
      const decryptStart = performance.now()
      await this.decryptMessage(encrypted, teamKey)
      const decryptEnd = performance.now()
      decryptTimes.push(decryptEnd - decryptStart)
    }

    const totalEncryptTime = encryptTimes.reduce((sum, time) => sum + time, 0)
    const totalDecryptTime = decryptTimes.reduce((sum, time) => sum + time, 0)

    return {
      encryptTime: totalEncryptTime,
      decryptTime: totalDecryptTime,
      avgEncryptTime: totalEncryptTime / iterations,
      avgDecryptTime: totalDecryptTime / iterations
    }
  }
}
